package com.lancoo.ccas52.arrangecourse.common;

import lombok.extern.slf4j.Slf4j;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @Description 工具类
 * @Author Fortysun
 * @Date 2023-02-17 8:36
 */
@Slf4j
public class BinaryUtil {

    /**
     * 二进制或操作
     * @param classWeek1 周次二进制字符串
     * @param classWeek2 另一个周次二进制字符串
     * @return
     */
    public static String binaryOR(String classWeek1, String classWeek2) {
        String result = "";
        try {
            //将二进制字符串转成十进制整型
            int week1 = Integer.parseInt(classWeek1, 2);
            int week2 = Integer.parseInt(classWeek2, 2);

            //计算二级制的长度
            int length = classWeek1.length();

            //将两个整型进行与操作
            int convertResult = week1|week2;

            //按照原始的长度，不够位的补0
            result = binaryNagateAddZero(convertResult, length);

        } catch (Exception ex) {
            log.error("BinaryUtil--->> binaryOR---->>Excetption:binary convert to decimal failed ~!!", ex);

            return result;
        }
        return result;
    }

    /**
     * 二进制异或操作
     * @param classWeek1 周次二进制字符串
     * @param classWeek2 另一个周次二进制字符串
     * @return
     */
    public static String binaryXOR(String classWeek1, String classWeek2) {
        String result = "";
        try {
            //将二进制字符串转成十进制整型
            int week1 = Integer.parseInt(classWeek1, 2);
            int week2 = Integer.parseInt(classWeek2, 2);

            //计算二级制的长度
            int length = classWeek1.length();

            //将两个整型进行与操作
            int convertResult = week1^week2;

            //按照原始的长度，不够位的补0
            result = binaryNagateAddZero(convertResult, length);

        } catch (Exception ex) {
            log.error("BinaryUtil--->> binaryXOR---->>Excetption:binary convert to decimal failed ~!!", ex);

            return result;
        }
        return result;
    }

    //返回两个二进制字符串，与操作之后的结果
    //并且根据传入字符串的长度，做了补零操作
    public static String binaryAnd(String classWeek1, String classWeek2) {
        String result = "";
        try {
            //将二进制字符串转成十进制整型
            int week1 = Integer.parseInt(classWeek1, 2);
            int week2 = Integer.parseInt(classWeek2, 2);

            //计算二级制的长度
            int length = classWeek1.length();

            //将两个整型进行与操作
            int convertResult = week1&week2;

            //按照原始的长度，不够位的补0
            result = binaryNagateAddZero(convertResult, length);

        } catch (Exception ex) {
            log.error("BinaryUtil--->> binaryAnd---->>Excetption:binary convert to decimal failed ~!!",ex);

            return result;
        }
        return result;
    }

    /**
     * 判断教室的周次，能够分配给班级
     * @param roomWeek 教室的周次
     * @param classWeek 班级所需的周次
     * @return
     */
    public static boolean binaryAndResultEqual(String roomWeek, String classWeek) {
        try {
            //将二进制字符串转成十进制整型
            int rWeek = Integer.parseInt(roomWeek, 2);
            int cWeek = Integer.parseInt(classWeek, 2);

            //将两个整型进行与操作
            int convertResult = rWeek&cWeek;
            //返回结果与班级所需周次是否相等，相等说明是可以分配的
            return cWeek==convertResult;

        } catch (Exception ex) {
            log.error("BinaryUtil--->> binaryAndResultEqual---->>Excetption:binary convert to decimal failed ~!!",ex);

            return false;
        }
    }

    //返回两个二进制字符串，与操作之后的结果（注意，如果字符串超过32位，将返回负数，一般学校的上课周数不会超过32位）
    public static Integer binaryAndInt(String classWeek1, String classWeek2) {
        int convertResult = 0;
        try {
            //将二进制字符串转成十进制整型
            int week1 = Integer.parseInt(classWeek1, 2);
            int week2 = Integer.parseInt(classWeek2, 2);


            //将两个整型进行与操作
            convertResult = week1&week2;
            return convertResult;

        } catch (Exception ex) {
            log.error("BinaryUtil--->> binaryAndInt---->>Excetption:binary convert to decimal failed ~!!",ex);

            return convertResult;
        }
    }

    //将二进制字符串中1和0对调，原来是1变成0，原来是0变成1
    public static String swapBinaryZeroAndOne(String binaryStr) {
        //如果字符串为空，直接返回
        if (binaryStr.isEmpty()) {
            return binaryStr;
        }

        char[] tempArr = binaryStr.toCharArray();
        for (int index = 0; index < tempArr.length; index++) {
            if (tempArr[index] == '0') {
                tempArr[index] = '1';
            } else if (tempArr[index] == '1') {
                tempArr[index] = '0';
            }
        }

        return String.valueOf(tempArr);
    }

    /**
     * 将原来二进制字符串中的一半的“1”，间隔改为0
     * 如：11011011，改为01001001，即第1、3、5、7...次出现的1，改成0
     * @param binaryStr
     * @return
     */
    public static String swapHalfZeroAndOne(String binaryStr) {
        //如果字符串为空，直接返回
        if (binaryStr.isEmpty()) {
            return binaryStr;
        }

        char[] tempArr = binaryStr.toCharArray();
        //用来统计1是第几次出现
        int count = 0;
        for (int index = 0; index < tempArr.length; index++) {
            //如果为1
            if (tempArr[index] == '1') {
                //则判断是不是第奇数次出现
                //是的话，就将1改成0
                if (count % 2 == 0) {
                    tempArr[index] = '0';
                }
                count++;
            }
        }

        return String.valueOf(tempArr);
    }

    /**
     * 将原来二进制字符串中的“1”，每多少周，保留几个1
     * 如，连上节次是4，周学时是6，6%4=2，那么需要每4周，排2次课，即period=4，num=2
     * 每4周将对应的1，随机挑2个改成0
     * @param period 周期
     * @param num 周期内取num个数为1，其他为0
     * @param binaryStr 二进制字符串
     * @return
     */
    public static String swapOneToZero(String binaryStr, Integer period, Integer num) {
        //如果字符串为空，或者周期值小于等于0， 直接返回
        if (binaryStr.isEmpty() || period <= 0) {
            return binaryStr;
        }
        //用来存储每轮循环，即每4周，需要出现0和1的顺序，比如我们需要4个里面取两个出来
        //那么先生成一个的整型列表；
        List<Character> periodList = new ArrayList<>();
        //整型列表，记录每4周，哪2周是1，哪两周是0
        for (int i = 0; i < period; i++) {
            if (i < num) {
                periodList.add('1');//每四周安排2个1
            }
            else{
                periodList.add('0');//剩下的不安排，设为0
            }
        }
        //将列表进行乱序,取前num位，作为需要标记的位置
        Collections.shuffle(periodList);

        char[] tempArr = binaryStr.toCharArray();
        //记录二进制字符串的长度，比如是16
        int length = tempArr.length;

        for (int index = 0; index < length; index++) {
            //每隔period，设置一轮周次信息
            //比如学期总周数是16，需要进行每4周安排2次课程
            //那么将0-15，拆成0-3、4-7、8-11、12-15
            //每个小周期，都按periodList存储的顺序进行赋值
            //比如periodList存的是0110
            //那么总的周次，应该是0110 0110 0110 0110
            tempArr[index] = periodList.get(index%period);
        }

        return String.valueOf(tempArr);
    }

    /**
     * 补零操作
     * @param srcValue 转换成二进制的，整数
     * @param binaryLength 要转换的二进制字符穿的最终长度
     * @return
     */
    private static String binaryNagateAddZero(int srcValue, int binaryLength) {
        String result = "";
        try {
            BigInteger src = new BigInteger("" + srcValue);//转换为BigInteger类型
            //将结果转换成二进制字符串
            result = src.toString(2);

            //转换出来的二进制，与原来二进制的长度不一致；
            //比如原来是18位，转换出来的是16位
            //记录位数差值
            int dec = binaryLength - result.length();

            if (dec > 0) {
                //将转换后的前面进行补0，差值是几，就补几个零
                for (int i = 0; i < dec; i++) {
                    result = "0" + result;
                }
            }
        } catch (Exception ex) {
            log.error("BinaryUtil--->> binaryNagateAddZero---->>Excetption:decimal convert to binary failed ~!!",ex);
        }
        return result;
    }

}
